/*
	Copyright (C) 2011 Daniel C. Dennison (dandennison84@gmail.com).  All rights reserved.
	This file is licensed under the EPL 1.0 (Eclipse Public License 1.0)
	A copy of the license can be found at http://www.eclipse.org/legal/epl-v10.html
*/

package com.gmail.dandennison84.accounting

class ChartOfAccountsBuilder extends NodeBuilder {
	final List<Account> accounts = []

	@Override
	protected void nodeCompleted(Object parent, Object node) {
		if(node.name() == 'groups') {
			// process each child in the order it is declared
			// to ensure Accounts are added sequentially
			node.children().each {
				if(isGroup(it)) {
					addGroupAndChildrenOfGroup(it)
				} else if(isAccount(it)) {
					addChild(it)
				}
			}
			node.children().each{configureContras(it)}
			node.children().findAll{isGroup(it)}?.each {
				configureContras(it)
			}
		}
	}

	private void addGroupAndChildrenOfGroup(Node groupNode) {
		AccountType accountType = groupNode.attribute('accountType')
		accountType = accountType ?: groupNode.parent().attribute('accountType')
		assert accountType
		addChildren(groupNode)

	}

	private void addChildren(Node node) {
		node.children().findAll{isAccount(it)}?.each {
			addChild(it)
		}
	}

	private void addChild(Node node) {
		Map attributes = getAttributes(node)
		accounts.add(new Account(attributes))
		addChildren(node)
	}

	private Map getAttributes(Node node) {
		Map attributes = [:]
		Node groupNode = findGroupNode(node)
		if(groupNode) {
			attributes.put('groupName', groupNode.name())
		}
		groupNode = groupNode?:node.parent()
		assert groupNode
		if(groupNode.parent()) {
			attributes.putAll(groupNode.parent().attributes())
		}
		attributes.putAll(groupNode.attributes())
		attributes.putAll(node.attributes())
		node.children()?.each {
			attributes.put(it.name(), it.value())
		}
		attributes.put('properties', getProperties(node))
		attributes
	}

	private void configureContras(Node node) {
		node.children().findAll{it.name() == 'account' && it.attribute('contraName')}?.each {
			String contraName = it.attribute('contraName')
			Account account = accounts.find{child -> child.name == it.attribute('name')}
			assert account
			account.contraAccount = accounts.find{it.name == contraName}
		}
	}

	private boolean isAccount(Node node) {
		node.name() == 'account' || node.name() == 'contra'
	}

	private boolean isGroup(Node node) {
		node.name() != 'groups' && node.name() != 'account' && node.name() != 'contra'
	}

	private Map getProperties(Node node) {
		Map properties = [:]
		node.children().each {
			if(it.name() == 'properties') {
				it.value().each {
					properties.put(it.name(), it.value())
				}
			}
		}
		properties
	}

	private Node findGroupNode(Node node) {
		while(node && !isGroup(node)) {
			node = node.parent()
		}
		node
	}
}
